package cn.qingyun.gis.modules.express.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.express.entity.ExpressRegion;
import cn.qingyun.gis.modules.express.entity.dto.ExpressRegionNodeDto;
import cn.qingyun.gis.modules.express.mapper.ExpressRegionMapper;
import cn.qingyun.gis.modules.express.request.ExpressRegionRequest;
import cn.qingyun.gis.modules.express.service.IExpressRegionService;
import cn.qingyun.gis.modules.express.util.LocationTool;
import cn.qingyun.gis.modules.express.util.SyncTool;
import cn.qingyun.gis.modules.init.system.entity.SysLog;
import cn.qingyun.gis.modules.init.system.mapper.SysLogMapper;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.util.GeometryFixer;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 快递业务规划数据 服务实现类
 * </p>
 *
 * @author miyunguo
 * @since 2023-08-28
 */
@Service
@Slf4j
@DS("express")
public class ExpressRegionServiceImpl extends ServiceImpl<ExpressRegionMapper, ExpressRegion> implements IExpressRegionService {

    @Resource
    private ExpressRegionMapper expressRegionMapper;
    @Resource
    private SysLogMapper sysLogMapper;

    @Override
    public List<ExpressRegionNodeDto> getRegionList(ExpressRegionRequest request) {
        //根据网格名称查询编码
        QueryWrapper<ExpressRegion> parentQueryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isEmpty(request.getCode())) {
            parentQueryWrapper.lambda().select(ExpressRegion::getCode).eq(ExpressRegion::getName, request.getName());
        }
        if (ObjectUtil.isNotEmpty(request.getCode())) {
            parentQueryWrapper.lambda().
                    select(ExpressRegion::getCode).
                    eq(ExpressRegion::getName, request.getName()).
                    eq(ExpressRegion::getCode, request.getCode());
        }
        ExpressRegion parentRegion = expressRegionMapper.selectOne(parentQueryWrapper);
        //根据父节点编码查询下属子节点编码
        QueryWrapper<ExpressRegion> sonQueryWrapper = new QueryWrapper<>();
        sonQueryWrapper.lambda().
                select(ExpressRegion::getId, ExpressRegion::getCode, ExpressRegion::getName).
                eq(ExpressRegion::getParentCode, parentRegion.getCode());
        List<ExpressRegion> regionList = expressRegionMapper.selectList(sonQueryWrapper);
        List<ExpressRegionNodeDto> nodeDtoList = Lists.newArrayList();
        for (ExpressRegion expressRegion : regionList) {
            ExpressRegionNodeDto nodeDto = new ExpressRegionNodeDto();
            BeanUtil.copyProperties(expressRegion, nodeDto);
            nodeDtoList.add(nodeDto);
        }
        return nodeDtoList;
    }

    @Override
    @Transactional
    public void wkt84To02() {
        int batchSize = 500; //批次
        int pages = (this.count() / batchSize) + 1;
        long start = System.currentTimeMillis();

        //并发导入
        SyncTool.doSync(pages, i -> {
            Page<ExpressRegion> page = new Page<>(i, 500);
            List<ExpressRegion> expressRegions = this.baseMapper.selectPage(page, null).getRecords();
            // 将边界类型进行 84 转 02
            for (ExpressRegion region : expressRegions) {
                region.setGeometry(LocationTool.convertWkts(region.getGeometry()));
            }
            this.updateBatchById(expressRegions);
        });

        // 进行日志记录
        SysLog sysLog = new SysLog(); // 创建日志对象
        long finish = System.currentTimeMillis();
        sysLog.setLogContent("业务区划84->02成功");
        sysLog.setCreateTime(DateUtil.date());
        sysLog.setLogType(3); //定时任务日志类型
        sysLog.setCreateBy("system");
        sysLog.setCostTime(finish - start);
        sysLog.setUserid("SFTP服务");
        sysLogMapper.insert(sysLog);
    }

    @Override
    @Transactional
    public void repairInvalidBoundary() {
        QueryWrapper<ExpressRegion> queryWrapper = new QueryWrapper<>();
        List<ExpressRegion> regions = expressRegionMapper.selectList(queryWrapper.lambda().isNotNull(ExpressRegion::getGeometry).ne(ExpressRegion::getGeometry, ""));
        List<ExpressRegion> repairRegions = Lists.newArrayList();
        long start = System.currentTimeMillis();
        Geometry geometry = null;
        try {
            for (ExpressRegion region : regions) {

                geometry = new WKTReader().read(region.getGeometry());
                if (!geometry.isValid()) {
                    log.debug("无效编码->" + region.getCode());
                    // 边界修复
                    String res = GeometryFixer.fix(geometry, true).toString();
                    // 此边界无法进行修复
                    if (res.equals("POLYGON EMPTY") || res.equals("MULTIPOLYGON EMPTY")) {
                        region.setGeometry(""); // 将边界清空
                    } else {
                        region.setGeometry(res);
                    }
                    repairRegions.add(region);

                }

                if (!repairRegions.isEmpty()) {
                    this.updateBatchById(repairRegions);
                }
            }
        } catch (ParseException e) {
            throw new CommonException("边界转换失败");
        } finally {
            // 进行日志记录
            SysLog sysLog = new SysLog(); // 创建日志对象
            long finish = System.currentTimeMillis();
            sysLog.setLogContent("业务区划边界修复成功");
            sysLog.setCreateTime(DateUtil.date());
            sysLog.setLogType(3); //定时任务日志类型
            sysLog.setCreateBy("system");
            sysLog.setCostTime(finish - start);
            sysLog.setUserid("定时任务");
            sysLogMapper.insert(sysLog);
        }
    }

    @Override
    public String randomGetGrid(String gridIds) {
        if (CharSequenceUtil.isEmpty(gridIds)) {
            throw new CommonException("传入业务区域编码不能为空~");
        }
        List<String> gridIdList = Arrays.asList(gridIds.split(","));
        if (gridIdList.contains("SD")) {
            return "531.0014.d02"; // 全省级别默认大观园网格
        }

        QueryWrapper<ExpressRegion> queryWrapper = new QueryWrapper<>();
        if (gridIdList.size() == 1 && Objects.equals(expressRegionMapper.selectOne(queryWrapper.lambda().select(ExpressRegion::getLevel).eq(ExpressRegion::getCode, gridIdList.get(0))).getLevel(), "4")) {
            return gridIdList.get(0);
        }

        String code = gridIdList.get(0);
        String level = expressRegionMapper.selectOne(queryWrapper.lambda().select(ExpressRegion::getLevel).eq(ExpressRegion::getCode, code)).getLevel();
        if ("2".equals(level)) { // 如果为地市
            QueryWrapper<ExpressRegion> cityQueryWrapper = new QueryWrapper<>();
            String disCode = expressRegionMapper.selectList(cityQueryWrapper.lambda().select(ExpressRegion::getCode).eq(ExpressRegion::getParentCode, code).orderByAsc(ExpressRegion::getCode)).get(0).getCode();
            QueryWrapper<ExpressRegion> disQueryWrapper = new QueryWrapper<>();
            return expressRegionMapper.selectList(disQueryWrapper.lambda().select(ExpressRegion::getCode).eq(ExpressRegion::getParentCode, disCode).orderByAsc(ExpressRegion::getCode)).get(0).getCode();

        }
        if ("3".equals(level)) { // 如果为区县
            QueryWrapper<ExpressRegion> disQueryWrapper = new QueryWrapper<>();
            return expressRegionMapper.selectList(disQueryWrapper.lambda().select(ExpressRegion::getCode).eq(ExpressRegion::getParentCode, code).orderByAsc(ExpressRegion::getCode)).get(0).getCode();
        }
        return code; // 为网格
    }

    @Override
    public List<String> pointInPolygon(String point) {
        return expressRegionMapper.pointInPolygon(point);
    }

    @Override
    public List<String> selectCode(BigDecimal lon, BigDecimal lat) {
        QueryWrapper<ExpressRegion> wrapper = new QueryWrapper<>();
        wrapper.eq("level", 4)
                .orderByAsc(String.format("ST_Distance(ST_GeomFromText('POINT(%s %s)', 4326), ST_GeomFromText(geometry, 4326))", lon, lat))
                .last("limit 1");
        List<ExpressRegion> expressRegions = expressRegionMapper.selectList(wrapper);
        return expressRegions.stream().map(ExpressRegion::getCode).collect(Collectors.toList());
    }
}
